home *** CD-ROM | disk | FTP | other *** search
/ Experimental BBS Explossion 3 / Experimental BBS Explossion III.iso / games / nhak_src.zip / PAGER.C < prev    next >
C/C++ Source or Header  |  1993-03-16  |  30KB  |  1,278 lines

  1. /*    SCCS Id: @(#)pager.c    3.0    89/11/19
  2. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  3. /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5. /* This file contains the command routine dowhatis() and a pager. */
  6. /* Also readmail() and doshell(), and generally the things that
  7.    contact the outside world. */
  8.  
  9. #define MONATTK_H    /* comment line for pre-compiled headers */
  10. /* block some unused #defines to avoid overloading some cpp's */
  11. #include    "hack.h"
  12.  
  13. #include <ctype.h>
  14. #ifndef NO_SIGNAL
  15. #include <signal.h>
  16. #endif
  17. #if defined(BSD) || defined(ULTRIX)
  18. #include <sys/wait.h>
  19. #endif
  20. #ifdef MACOS
  21. extern WindowPtr    HackWindow;
  22. extern short macflags;
  23. #endif
  24.  
  25. #ifndef SEEK_SET
  26. #define SEEK_SET 0
  27. #endif
  28.  
  29. STATIC_DCL boolean FDECL(clear_help, (CHAR_P));
  30. STATIC_DCL boolean FDECL(valid_help, (CHAR_P));
  31.  
  32. #ifndef OVLB
  33. STATIC_DCL char hc;
  34. #else /* OVLB */
  35. STATIC_OVL char NEARDATA hc = 0;
  36.  
  37. static void FDECL(page_more, (FILE *,int));
  38. static boolean FDECL(is_swallow_sym, (UCHAR_P));
  39. static boolean FDECL(pmatch,(const char *,const char *));
  40. static boolean FDECL(outspec,(const char *,int));
  41. static const char *FDECL(lookat,(int,int,UCHAR_P));
  42. #ifdef WIZARD
  43. static void NDECL(wiz_help);
  44. #endif
  45. static void NDECL(help_menu);
  46.  
  47. /*
  48.  * simple pattern matcher: '*' matches 0 or more characters
  49.  * returns TRUE if strng matches patrn
  50.  */
  51.  
  52. static boolean
  53. pmatch(patrn, strng)
  54.     const char *patrn, *strng;
  55. {
  56.     char s, p;
  57.  
  58.     s = *strng;
  59.     p = *patrn;
  60.  
  61.     if (!p) {
  62.         return (s == 0);
  63.     }
  64.  
  65.     if (p == '*') {
  66.         if (!patrn[1] || pmatch(patrn+1, strng)) {
  67.             return TRUE;
  68.         }
  69.         return (s ? pmatch(patrn, strng+1) : FALSE);
  70.     }
  71.  
  72.     return (p == s) ? pmatch(patrn+1, strng+1) : FALSE;
  73. }
  74.  
  75. /*
  76.  * returns "true" for characters that could represent a monster's stomach
  77.  */
  78.  
  79. static boolean
  80. is_swallow_sym(c)
  81. uchar c;
  82. {
  83.     return (index(" /-\\|", (char)c) != 0);
  84. }
  85.  
  86. /*
  87.  * print out another possibility for dowhatis. "new" is the possible new
  88.  * string; "out_flag" indicates whether we really want output, and if
  89.  * so what kind of output: 0 == no output, 1 == "(or %s)" output. 
  90.  * Returns TRUE if this new string wasn't the last string printed.
  91.  */
  92.  
  93. static boolean
  94. outspec(new, out_flag)
  95. const char *new;
  96. int out_flag;
  97. {
  98.     static char NEARDATA old[50];
  99.  
  100.     if (!strcmp(old, new))
  101.         return FALSE;        /* don't print the same thing twice */
  102.  
  103.     if (out_flag)
  104.         pline("(or %s)", an(new));
  105.  
  106.     Strcpy(old, new);
  107.     return 1;
  108. }
  109.  
  110. /*
  111.  * return the name of the character ch found at (x,y)
  112.  */
  113.  
  114. static
  115. const char *
  116. lookat(x, y, ch)
  117. int x,y;
  118. uchar ch;
  119. {
  120.     register struct monst *mtmp;
  121.     register struct obj *otmp;
  122.     struct trap *trap;
  123.     static char NEARDATA answer[50];
  124.     register char *s, *t;
  125.     uchar typ;
  126.  
  127.     answer[0] = 0;
  128.  
  129.     if(MON_AT(x,y)) {
  130.         mtmp = m_at(x,y);
  131.         if (!showmon(mtmp) || Hallucination)
  132.             mtmp = (struct monst *)0;
  133.     } else
  134.         mtmp = (struct monst *) 0;
  135.     typ = levl[x][y].typ;
  136.     if (!Invisible 
  137. #ifdef POLYSELF
  138.             && !u.uundetected
  139. #endif
  140.             && u.ux==x && u.uy==y) {
  141.         Sprintf(answer, "%s named %s",
  142. #ifdef POLYSELF
  143.             u.mtimedone ? mons[u.umonnum].mname :
  144. #endif
  145.             pl_character, plname);
  146.     } else if (u.uswallow && is_swallow_sym(ch)) {
  147.         Sprintf(answer, "interior of %s", defmonnam(u.ustuck));
  148.     } else if (mtmp && !mtmp->mimic)
  149.         Sprintf(answer, "%s%s",
  150.            mtmp->mtame ? "tame " :
  151.            mtmp->mpeaceful ? "peaceful " : "",
  152.            strncmp(lmonnam(mtmp), "the ", 4)
  153.               ? lmonnam(mtmp) : lmonnam(mtmp)+4);
  154.     else if (!levl[x][y].seen)
  155.         Strcpy(answer,"dark part of a room");
  156.     else if (mtmp && mtmp->mimic) {
  157.         if (mtmp->m_ap_type == M_AP_FURNITURE) {
  158.             if (mtmp->mappearance == S_altar)
  159.                 Strcpy(answer, "neutral altar");
  160.             else
  161.                 Strcpy(answer, explainsyms[mtmp->mappearance]);
  162.         }
  163.         else if (mtmp->m_ap_type == M_AP_OBJECT) {
  164.             if (mtmp->mappearance == STRANGE_OBJECT)
  165.                 Strcpy(answer, "strange object");
  166.             else {
  167.                 int oindx = mtmp->mappearance;
  168.                 otmp = mksobj(oindx,FALSE );
  169.                 if(oindx == STATUE || oindx == FIGURINE)
  170.                     otmp->corpsenm = PM_KOBOLD;
  171.                 else if (oindx == DRAGON_SCALE_MAIL)
  172.                     otmp->corpsenm = PM_RED_DRAGON;
  173.                 Strcpy(answer, distant_name(otmp, xname));
  174.                 free((genericptr_t) otmp);
  175.             }
  176.         }
  177.         else if (mtmp->m_ap_type == M_AP_GOLD)
  178.             Strcpy(answer, "pile of gold");
  179.     }
  180.     else if (OBJ_AT(x, y)) {
  181.         otmp = level.objects[x][y];
  182.         Strcpy(answer, distant_name(otmp, xname));
  183.     }
  184.     else if (ch == GOLD_SYM) {
  185.         Strcpy(answer, "pile of gold");
  186.     }
  187. #ifdef ALTARS
  188.     else if (ch == ALTAR_SYM && IS_ALTAR(typ)) {
  189.         int kind = levl[x][y].altarmask & ~A_SHRINE;
  190.         Sprintf( answer, "%s altar",
  191.             (kind == A_CHAOS) ? "chaotic" :
  192.             (kind == A_NEUTRAL) ? "neutral" :
  193.              "lawful" );
  194.     }
  195. #endif
  196. #ifdef STRONGHOLD
  197.     else if ((ch == DB_VWALL_SYM || ch == DB_HWALL_SYM) && is_db_wall(x,y))
  198.         Strcpy(answer,"raised drawbridge");
  199. #endif
  200. #ifdef THRONES
  201.     else if ((ch == THRONE_SYM) && IS_THRONE(typ))
  202.         Strcpy(answer, "throne");
  203. #endif
  204.     else if ( (ch==H_OPEN_DOOR_SYM ||
  205.            ch==V_OPEN_DOOR_SYM ||
  206.            ch==CLOSED_DOOR_SYM ||
  207.            ch==NO_DOOR_SYM) &&
  208.           IS_DOOR(typ) ) {
  209.         switch(levl[x][y].doormask & ~D_TRAPPED) {
  210.             case D_NODOOR: Strcpy(answer,"doorway"); break;
  211.             case D_BROKEN: Strcpy(answer,"broken door"); break;
  212.             case D_ISOPEN: Strcpy(answer,"open door"); break;
  213.             default:       Strcpy(answer,"closed door"); break;
  214.                        /* locked or not */
  215.         }
  216.     }
  217. #ifdef SINKS
  218.     else if (ch == SINK_SYM && IS_SINK(levl[x][y].typ))
  219.         Strcpy(answer,"sink");
  220. #endif
  221.     else if ((ch == TRAP_SYM || ch == WEB_SYM) && (trap = t_at(x, y))) {
  222.         if (trap->ttyp == WEB && ch == WEB_SYM)
  223.             Strcpy(answer, "web");
  224.         else if (trap->ttyp != MONST_TRAP && ch == TRAP_SYM) {
  225.             Strcpy(answer, traps[ Hallucination ?
  226.                 rn2(TRAPNUM-3)+3 : trap->ttyp]);
  227.         /* strip leading garbage */
  228.             for (s = answer; *s && *s != ' '; s++) ;
  229.             if (*s) ++s;
  230.             for (t = answer; *t++ = *s++; ) ;
  231.         }
  232.     }
  233.     else if (ch == UP_SYM && x == xupstair && y == yupstair)
  234.         Strcpy(answer, "staircase up");
  235.     else if (ch == DN_SYM && x == xdnstair && y == ydnstair)
  236.         Strcpy(answer, "staircase down");
  237. #ifdef STRONGHOLD
  238.     else if (ch == UPLADDER_SYM && x && x == xupladder && y == ydnladder)
  239.         Strcpy(answer, "ladder up");
  240.     else if (ch == DNLADDER_SYM && x && x == xdnladder && y == ydnladder)
  241.         Strcpy(answer, "ladder down");
  242. #endif
  243.     else if (IS_ROOM(typ)) {
  244.         if (ch == ROOM_SYM) {
  245.             if (levl[x][y].icedpool)
  246.                 Strcpy(answer,"iced pool");
  247.             else
  248.                 Strcpy(answer,"floor of a room");
  249.         }
  250.         else if (ch == STONE_SYM || ch == ' ')
  251.             Strcpy(answer,"dark part of a room");
  252.     }
  253.     else if (ch == CORR_SYM && SPACE_POS(typ))
  254.         Strcpy(answer,"corridor");
  255.     else if (!ACCESSIBLE(typ)) {
  256.         if (ch == STONE_SYM || ch == ' ')
  257.             Strcpy(answer,"dark part of a room");
  258.         else
  259.             Strcpy(answer,"wall");
  260.     }
  261.     return answer;
  262. }
  263.  
  264.     
  265. int
  266. dowhatis()
  267. {
  268.     FILE *fp;
  269.     char buf[BUFSZ], inpbuf[BUFSZ];
  270.     register char *ep, *inp = inpbuf;
  271.     char *alt = 0;        /* alternate description */
  272. #ifdef __GNULINT__
  273.     const char *firstmatch = 0;
  274. #else
  275.     const char *firstmatch;
  276. #endif
  277.     uchar q;
  278.     register int i;
  279.     coord    cc;
  280.     boolean oldverb = flags.verbose;
  281.     boolean found_in_file = FALSE, need_to_print = FALSE;
  282.     int    found = 0;
  283.     static const char *mon_interior = "the interior of a monster";
  284.  
  285. #ifdef OS2_CODEVIEW
  286.     char tmp[PATHLEN];
  287.  
  288.     Strcpy(tmp,hackdir);
  289.     append_slash(tmp);
  290.     Strcat(tmp,DATAFILE);
  291.     fp = fopen(tmp,"r"));
  292. #else
  293.     fp = fopen(DATAFILE, "r");
  294. #endif
  295.     if(!fp) {
  296. #ifdef MACOS
  297.         fp = openFile(DATAFILE, "r");
  298.     }
  299.     if (!fp) {
  300. #endif
  301.         pline("Cannot open data file!");
  302.         return 0;
  303.     }
  304.  
  305.     pline ("Specify unknown object by cursor? ");
  306.     q = ynq();
  307.     if (q == 'q') {
  308.         (void) fclose(fp);
  309.         return 0;
  310.     } else if (q == 'n') {
  311.         cc.x = cc.y = -1;
  312.         pline("Specify what? (type the word) ");
  313.         getlin(inp);
  314.         if (inp[0] == '\033' || !inp[0]) {
  315.             (void)fclose(fp);
  316.             return 0;
  317.         }
  318.         if (!inp[1])
  319.             q = inp[0];
  320.         else
  321.             q = 0;
  322.     } else {
  323.         cc.x = u.ux;
  324.         cc.y = u.uy;
  325. selobj:
  326.         need_to_print = found_in_file = FALSE;
  327.         found = 0;
  328.         inp = inpbuf;
  329.         alt = 0;
  330.         (void) outspec("", 0);        /* reset output */
  331.         if(flags.verbose)
  332.             pline("Please move the cursor to an unknown object.");
  333.         else
  334.             pline("Pick an object.");
  335.         getpos(&cc, FALSE, "an unknown object");
  336.         if (cc.x < 0) {
  337.                 (void) fclose(fp); /* sweet@scubed */
  338.                 flags.verbose = oldverb;
  339.                 return 0;
  340.         }
  341.         flags.verbose = FALSE;
  342.         if (!u.uswallow) {
  343.             q = levl[cc.x][cc.y].scrsym;
  344.             if (!q || (!levl[cc.x][cc.y].seen && !MON_AT(cc.x,cc.y)))
  345.                 q = ' ';
  346.         }
  347.         else if (cc.x == u.ux && cc.y == u.uy)
  348.             q = u.usym;
  349.         else {
  350.             i = (u.uy - cc.y)+1;
  351.             if (i < 0 || i > 2)
  352.                 q = ' ';
  353.             else {
  354.                 firstmatch = (i == 0) ? "/-\\" :
  355.                     (i == 1) ? "| |" : "\\-/";
  356.                 i = (u.ux - cc.x)+1;
  357.                 if (i < 0 || i > 2)
  358.                     q = ' ';
  359.                 else
  360.                     q = firstmatch[i];
  361.             }
  362.         }
  363.     }
  364.  
  365.     if (!q)
  366.         goto checkfile; /* user typed in a complete string */
  367.  
  368.     if (q != ' ' && index(quitchars, (char)q)) {
  369.         (void) fclose(fp); /* sweet@scubed */
  370.         flags.verbose = oldverb;
  371.         return 0;
  372.     }
  373. /*
  374.  * if the user just typed one letter, or we're identifying from the
  375.  * screen, then we have to check all the possibilities and print them
  376.  * out for him/her.
  377.  */
  378.  
  379. /* Check for monsters */
  380.     for (i = 0; monsyms[i]; i++) {
  381.         if (q == monsyms[i]) {
  382.             need_to_print = TRUE;
  383.             pline("%c       %s",q,an(monexplain[i]));
  384.             (void) outspec(firstmatch = monexplain[i], 0);
  385.             found++;
  386.             break;
  387.         }
  388.     }
  389.  
  390. /* Special case: if identifying from the screen, and
  391.  * we're swallowed, and looking at something other than our own symbol,
  392.  * then just say "the interior of a monster".
  393.  */
  394.     if (u.uswallow && is_swallow_sym(q)) {
  395.         if (!found) {
  396.             pline("%c       %s", q, mon_interior);
  397.             (void)outspec(firstmatch=mon_interior, 0);
  398.         }
  399.         else
  400.             (void)outspec(mon_interior, 1);
  401.         found++; need_to_print = TRUE;
  402.     }
  403.  
  404. /* Now check for objects */
  405.     for (i = 0; objsyms[i]; i++) {
  406.         if (q == objsyms[i]) {
  407.             need_to_print = TRUE;
  408.             if (!found) {
  409.                 pline("%c       %s",q,an(objexplain[i]));
  410.                 (void)outspec(firstmatch = objexplain[i], 0);
  411.                 found++;
  412.             }
  413.             else if (outspec(objexplain[i], 1))
  414.                 found++;
  415.         }
  416.     }
  417.  
  418. /* Now check for graphics symbols */
  419.     for (i = 0; i < MAXPCHARS; i++) {
  420.         if ( q == showsyms[i] && (*explainsyms[i])) {
  421.             if (!found) {
  422.                 pline("%c       %s",q,an(explainsyms[i]));
  423.                 (void)outspec(firstmatch = explainsyms[i], 0);
  424.                 found++;
  425.             }
  426.             else if (outspec(explainsyms[i], 1))
  427.                 found++;
  428.             if (i == S_altar || i == S_trap || i == S_web)
  429.                 need_to_print = TRUE;
  430.         }
  431.     }
  432.  
  433.     if (!found)
  434.         pline("I've never heard of such things.");
  435.     else if (cc.x != -1) {    /* a specific object on screen */
  436.         if (found > 1 || need_to_print) {
  437.             Strcpy(inp, lookat(cc.x, cc.y, q));
  438.             if (*inp)
  439.                 pline("(%s)", inp);
  440.         }
  441.         else {
  442.             Strcpy(inp, firstmatch);
  443.         }
  444.     }
  445.     else if (found == 1) {
  446.         Strcpy(inp, firstmatch);
  447.     }
  448.     else
  449.         found = FALSE;    /* abort the 'More info?' stuff */
  450.  
  451. /* check the data file for information about this thing */
  452.  
  453. checkfile:
  454.  
  455.     if (!strncmp(inp, "interior of ", 12))
  456.         inp += 12;
  457.     if (!strncmp(inp, "a ", 2))
  458.         inp += 2;
  459.     else if (!strncmp(inp, "an ", 3))
  460.         inp += 3;
  461.     else if (!strncmp(inp, "the ", 4))
  462.         inp += 4;
  463.     if (!strncmp(inp, "tame ", 5))
  464.         inp += 5;
  465.     else if (!strncmp(inp, "peaceful ", 9))
  466.         inp += 9;
  467.     if (!strncmp(inp, "invisible ", 10))
  468.         inp += 10;
  469.  
  470. /*
  471.  * look in the file for more info if:
  472.  * the user typed in the whole name (!q)
  473.  * OR we've found a possible match with the character q (found) and
  474.  *    flags.help is TRUE
  475.  * and, of course, the name to look for must be non-empty.
  476.  */
  477.     if ((!q || (found && flags.help)) && *inp) {
  478. /* adjust the input to remove "named " and convert to lower case */
  479.          for (ep = inp; *ep; ) {
  480.             if ((!strncmp(ep, " named ", 7) && (alt = ep + 7)) ||
  481.                 !strncmp(ep, " called ", 8))
  482.                 *ep = 0;
  483.             else {
  484.                 if(isupper(*ep)) *ep = tolower(*ep);
  485.                 ep++;
  486.             }
  487.         }
  488.  
  489. /*
  490.  * If the object is named, then the name is the alternate search string;
  491.  * otherwise, the result of makesingular() applied to the name is. This
  492.  * isn't strictly optimal, but named objects of interest to the user
  493.  * will usually be found under their name, rather than under their
  494.  * object type, so looking for a singular form is pointless.
  495.  */
  496.  
  497.         if (!alt)
  498.             alt = makesingular(inp);
  499.         else
  500.             for (ep = alt; *ep; ep++) 
  501.                 if(isupper(*ep)) *ep = tolower(*ep);
  502.  
  503.         while(fgets(buf,BUFSZ,fp)) {
  504.             if(*buf != '\t') {
  505.                 ep = index(buf, '\n');
  506.                 if(ep) *ep = 0;
  507.                 else impossible("bad data file");
  508.                 if (pmatch(buf, inp)||(alt && pmatch(buf, alt))) {
  509.                 found_in_file = TRUE;
  510.                 break;
  511.                 }
  512.             }
  513.         }
  514.     }
  515.  
  516.     if(found_in_file) {
  517. /* skip over other possible matches for the info */
  518.         for(;;) {
  519.             if ( (i = getc(fp)) == '\t' ) {
  520.                 (void) ungetc(i, fp);
  521.                 break;
  522.             }
  523.             if (!fgets(buf, BUFSZ, fp)) {
  524.                 break;
  525.             }
  526.         }
  527.         if (q) {
  528.             pline("More info? ");
  529.             if(yn() == 'y') {
  530.                 page_more(fp,1); /* does fclose() */
  531.                 flags.verbose = oldverb;
  532.                 return 0;
  533.             }
  534.         }
  535.         else {
  536.             page_more(fp, 1);
  537.             flags.verbose = oldverb;
  538.             return 0;
  539.         }
  540.     }
  541.     else if (!q)
  542.         pline("I don't have any information on those things.");
  543.  
  544. /* if specified by cursor, keep going */
  545.     if(cc.x != -1) {
  546.         more();
  547.         rewind(fp);
  548.         goto selobj;
  549.     }
  550.     (void) fclose(fp);     /* kopper@psuvax1 */
  551.     flags.verbose = oldverb;
  552.     return 0;
  553. }
  554.  
  555. int
  556. dowhatdoes()
  557. {
  558.     FILE *fp;
  559.     char bufr[BUFSZ+6];
  560.     register char *buf = &bufr[6], *ep, q, ctrl, meta;
  561. #ifdef OS2_CODEVIEW
  562.     char tmp[PATHLEN];
  563.  
  564.     Strcpy(tmp,hackdir);
  565.     append_slash(tmp);
  566.     Strcat(tmp,CMDHELPFILE);
  567.     if(!(fp = fopen(tmp,"r"))) {
  568. #else
  569. # ifdef MACOS
  570.     if(!(fp = fopen(CMDHELPFILE, "r")))
  571.         fp = openFile(CMDHELPFILE, "r");
  572.     if (!fp) {
  573. # else
  574.     if(!(fp = fopen(CMDHELPFILE, "r"))) {
  575. # endif
  576. #endif
  577.         pline("Cannot open data file!");
  578.         return 0;
  579.     }
  580.  
  581.     pline("What command? ");
  582. #if defined(UNIX) || defined(VMS)
  583.     introff();
  584. #endif
  585.     q = readchar();
  586. #if defined(UNIX) || defined(VMS)
  587.     intron();
  588. #endif
  589.     ctrl = ((q <= '\033') ? (q - 1 + 'A') : 0);
  590.     meta = ((0x80 & q) ? (0x7f & q) : 0);
  591.     while(fgets(buf,BUFSZ,fp))
  592.         if ((ctrl && *buf=='^' && *(buf+1)==ctrl) ||
  593.         (meta && *buf=='M' && *(buf+1)=='-' && *(buf+2)==meta) ||
  594.         *buf==q) {
  595.         ep = index(buf, '\n');
  596.         if(ep) *ep = 0;
  597.         if (ctrl && buf[2] == '\t'){
  598.             buf = bufr + 1;
  599.             (void) strncpy(buf, "^?      ", 8);
  600.             buf[1] = ctrl;
  601.         } else if (meta && buf[3] == '\t'){
  602.             buf = bufr + 2;
  603.             (void) strncpy(buf, "M-?     ", 8);
  604.             buf[2] = meta;
  605.         } else if(buf[1] == '\t'){
  606.             buf = bufr;
  607.             buf[0] = q;
  608.             (void) strncpy(buf+1, "       ", 7);
  609.         }
  610.         pline("%s", buf);
  611.         (void) fclose(fp);
  612.         return 0;
  613.         }
  614.     pline("I've never heard of such commands.");
  615.     (void) fclose(fp);
  616.     return 0;
  617. }
  618.  
  619. /* make the paging of a file interruptible */
  620. static volatile int NEARDATA got_intrup;
  621.  
  622. #if !defined(MSDOS) && !defined(TOS) && !defined(MACOS)
  623. static int
  624. intruph(){
  625.     (void) signal(SIGINT, (SIG_RET_TYPE) intruph);
  626.     got_intrup++;
  627.     return 0;
  628. }
  629. #endif
  630.  
  631. /* simple pager, also used from dohelp() */
  632. static void
  633. page_more(fp,strip)
  634. FILE *fp;
  635. int strip;    /* nr of chars to be stripped from each line (0 or 1) */
  636. {
  637. #ifdef MACOS
  638.     short tmpflags;
  639.     
  640.     tmpflags = macflags;
  641.     macflags &= ~fDoUpdate;
  642.     if(!mac_more(fp, strip)) {
  643.         macflags |= (tmpflags & fDoUpdate);
  644.         return;
  645.     }
  646.     macflags |= (tmpflags & fDoUpdate);
  647. #else
  648.     register char *bufr;
  649. #if !defined(MSDOS) && !defined(MINIMAL_TERM)
  650.     register char *ep;
  651. #endif
  652. #if !defined(MSDOS) && !defined(TOS)
  653.     int (*prevsig)() = (int (*)())signal(SIGINT, (SIG_RET_TYPE) intruph);
  654. #endif
  655. #if defined(MSDOS) || defined(MINIMAL_TERM)
  656.     /* There seems to be a bug in ANSI.SYS  The first tab character
  657.      * after a clear screen sequence is not expanded correctly.  Thus
  658.      * expand the tabs by hand -dgk
  659.      */
  660.     int tabstop = 8, spaces;
  661.     char buf[BUFSIZ], *bufp, *bufrp;
  662.  
  663.     set_pager(0);
  664.     bufr = (char *) alloc((unsigned) COLNO);
  665.     while (fgets(buf, BUFSIZ, fp) && (!strip || *buf == '\t')){
  666.         bufp = buf;
  667.         bufrp = bufr;
  668.         if (strip && *bufp && *bufp != '\n')
  669.             *bufrp++ = *bufp++;
  670.         while (*bufp && *bufp != '\n') {
  671.             if (*bufp == '\t') {
  672.                 spaces = tabstop - (bufrp - bufr) % tabstop;
  673.                 while (spaces--)
  674.                     *bufrp++ = ' ';
  675.                 bufp++;
  676.             } else
  677.                 *bufrp++ = *bufp++;
  678.         }
  679.         *bufrp = '\0';
  680. #else /* MSDOS /**/
  681.     set_pager(0);
  682.     bufr = (char *) alloc((unsigned) COLNO);
  683.     bufr[COLNO-1] = 0;
  684.     while(fgets(bufr,COLNO-1,fp) && (!strip || *bufr == '\t')){
  685.         ep = index(bufr, '\n');
  686.         if(ep)
  687.             *ep = 0;
  688. #endif /* MSDOS /**/
  689.         if(got_intrup || page_line(bufr+strip)) {
  690.             set_pager(2);
  691.             goto ret;
  692.         }
  693.     }
  694.     set_pager(1);
  695. ret:
  696.     free((genericptr_t) bufr);
  697.     (void) fclose(fp);
  698. #if !defined(MSDOS) && !defined(TOS)
  699.     (void) signal(SIGINT, (SIG_RET_TYPE) prevsig);
  700.     got_intrup = 0;
  701. #endif
  702. #endif /* MACOS */
  703. }
  704.  
  705. #endif /* OVLB */
  706.  
  707. #define    PAGMIN    12    /* minimum # of lines for page below level map */
  708.  
  709. #ifndef OVLB
  710.  
  711. STATIC_DCL boolean whole_screen;
  712.  
  713. #else /* OVLB */
  714.  
  715. STATIC_OVL boolean NEARDATA whole_screen = TRUE;
  716.  
  717. void
  718. set_whole_screen() {    /* called in termcap as soon as LI is known */
  719.     whole_screen = (LI-ROWNO-2 <= PAGMIN || !CD);
  720. }
  721.  
  722. #ifdef NEWS
  723. int
  724. readnews() {
  725.     register int ret;
  726.  
  727.     whole_screen = TRUE;    /* force a docrt(), our first */
  728.     ret = page_file(NEWS, TRUE);
  729.     set_whole_screen();
  730.     return(ret);        /* report whether we did docrt() */
  731. }
  732. #endif
  733.  
  734. void
  735. set_pager(mode)
  736. register int mode;    /* 0: open  1: wait+close  2: close */
  737. {
  738. #ifdef LINT    /* lint may handle static decl poorly -- static boolean so; */
  739.     boolean so;
  740. #else
  741.     static boolean NEARDATA so;
  742. #endif
  743.     if(mode == 0) {
  744.         if(!whole_screen) {
  745.             /* clear topline */
  746.             clrlin();
  747.             /* use part of screen below level map */
  748.             curs(1, ROWNO+4);
  749.         } else {
  750.             cls();
  751.         }
  752.         so = flags.standout;
  753.         flags.standout = 1;
  754.     } else {
  755.         if(mode == 1) {
  756.             curs(1, LI);
  757.             more();
  758.         }
  759.         flags.standout = so;
  760.         if(whole_screen)
  761.             docrt();
  762.         else {
  763.             curs(1, ROWNO+4);
  764.             cl_eos();
  765.         }
  766.     }
  767. }
  768.  
  769. #endif /* OVLB */
  770. #ifdef OVL0
  771.  
  772. int
  773. page_line(s)        /* returns 1 if we should quit */
  774. register const char *s;
  775. {
  776. #ifdef CLIPPING
  777. /* we assume here that no data files have more than 80 chars/line */
  778.     static char tmp[81], *t;
  779. #endif
  780.     if(cury == LI-1) {
  781.         if(!*s)
  782.             return(0);    /* suppress blank lines at top */
  783.         (void) putchar('\n');
  784.         cury++;
  785.         cmore("q\033");
  786.         if(morc) {
  787.             morc = 0;
  788.             return(1);
  789.         }
  790.         if(whole_screen)
  791.             cls();
  792.         else {
  793.             curs(1, ROWNO+4);
  794.             cl_eos();
  795.         }
  796.     }
  797. #ifdef CLIPPING
  798. /* if lines are too long for the screen, first try stripping leading blanks */
  799.     if (strlen(s) >= CO) {
  800.         while (*s == ' ' || *s == '\t') s++;
  801.     }
  802.  
  803. /* if it's still too long, try compressing blanks */
  804.     if (strlen(s) >= CO) {
  805.         t = tmp;
  806.         while ( (*t = *s) != 0) {
  807.             if (*t == ' ') {
  808.                 while (*s == ' ')
  809.                     s++;
  810.             }
  811.             else
  812.                 s++;
  813.             t++;
  814.         }
  815.         s = tmp;
  816.     }
  817. #endif /* CLIPPING */
  818.  
  819. #ifdef TERMINFO
  820.     xputs(s); xputc('\n');
  821. #else
  822.     (void) puts(s);
  823. # ifdef MACOS
  824.     (void) putchar('\n');
  825. # endif
  826. #endif
  827.     cury++;
  828.     return(0);
  829. }
  830.  
  831. /*
  832.  * Flexible pager: feed it with a number of lines and it will decide
  833.  * whether these should be fed to the pager above, or displayed in a
  834.  * corner.
  835.  * Call:
  836.  *    cornline(0, title or 0)    : initialize
  837.  *    cornline(1, text)    : add text to the chain of texts
  838.  *    cornline(2, morcs)    : output everything and cleanup
  839.  *    cornline(3, 0)        : cleanup
  840.  *    cornline(-1,"")        : special, for help menu mode only
  841.  */
  842.  
  843. void
  844. cornline(mode, text)
  845. int mode;
  846. const char *text;
  847. {
  848.     static struct line {
  849.         struct line *next_line;
  850.         char *line_text;
  851.     } NEARDATA *texthead, NEARDATA *texttail;
  852.     static int NEARDATA maxlen;
  853.     static int NEARDATA linect;
  854.     register struct line *tl;
  855.     register boolean hmenu = FALSE;
  856.  
  857.     if(mode == -1) { /* help menu display only */
  858.         mode = 2;
  859.         hmenu = TRUE;
  860.     }
  861.     if(mode == 0) {
  862.         texthead = 0;
  863.         maxlen = 0;
  864.         linect = 0;
  865.         if(text) {
  866.             cornline(1, text);    /* title */
  867.             cornline(1, "");    /* blank line */
  868.         }
  869.         return;
  870.     }
  871.  
  872.     if(mode == 1) {
  873.         register int len;
  874.  
  875.         if(!text) return;    /* superfluous, just to be sure */
  876.         linect++;
  877.         len = strlen(text) + 1; /* allow for an extra leading space */
  878.         if(len > maxlen)
  879.         maxlen = len;
  880.         tl = (struct line *)
  881.         alloc((unsigned)(len + sizeof(struct line) + 1));
  882.         tl->next_line = 0;
  883.         tl->line_text = (char *)(tl + 1);
  884.         tl->line_text[0] = ' ';
  885.         tl->line_text[1] = '\0';
  886.         Strcat(tl->line_text, text);
  887.         if(!texthead)
  888.         texthead = tl;
  889.         else
  890.         texttail->next_line = tl;
  891.         texttail = tl;
  892.         return;
  893.     }
  894.  
  895.     /* --- now we really do it --- */
  896.     if(mode == 2 && linect == 1)                /* topline only */
  897.         pline("%s", texthead->line_text);
  898.     else
  899.     if(mode == 2) {
  900.         register int curline, lth;
  901.  
  902.         if(flags.toplin == 1) more();    /* ab@unido */
  903.         remember_topl();
  904.  
  905.         lth = CO - maxlen - 2;           /* Use full screen width */
  906.         if (linect < LI && lth >= 10) {             /* in a corner */
  907.         home ();
  908.         cl_end ();
  909.         flags.toplin = 0;
  910.         curline = 1;
  911.         for (tl = texthead; tl; tl = tl->next_line) {
  912. #if defined(MSDOS) && !defined(AMIGA)
  913.             cmov (lth, curline);
  914. #else
  915.             curs (lth, curline);
  916. #endif
  917.             if(curline > 1)
  918.             cl_end ();
  919.             xputs(tl->line_text);
  920.             curx = curx + strlen(tl->line_text);
  921.             curline++;
  922.         }
  923.         if(hmenu) {    /* help menu display */
  924.             do 
  925.                 hc = lowc(readchar());
  926.             while (!valid_help(hc));
  927.         }
  928. #if defined(MSDOS) && !defined(AMIGA)
  929.         cmov (lth, curline);
  930. #else
  931.         curs (lth, curline);
  932. #endif
  933.         cl_end ();
  934.         if (!hmenu) {
  935.             cmore (text);
  936.         }
  937.         if (!hmenu || clear_help(hc)) {
  938.             home ();
  939.             cl_end ();
  940.             docorner (lth, curline-1);
  941.         }
  942.         } else {                    /* feed to pager */
  943. #ifdef MACOS
  944.         short    tmpflags;
  945.         
  946.         tmpflags = macflags;
  947.         macflags &= ~fDoNonKeyEvt;
  948. #endif
  949.         set_pager(0);
  950.         for (tl = texthead; tl; tl = tl->next_line) {
  951.             if (page_line (tl->line_text)) {
  952.             set_pager(2);
  953. #ifdef MACOS
  954.             macflags = tmpflags;
  955. #endif
  956.             while(tl = texthead) {
  957.                 texthead = tl->next_line;
  958.                 free((genericptr_t) tl);
  959.             }
  960.             return;
  961.             }
  962.         }
  963.         if(text) {
  964.             cgetret(text);
  965.             set_pager(2);
  966.         } else
  967.             set_pager(1);
  968. #ifdef MACOS
  969.         macflags = tmpflags;
  970. #endif
  971.         }
  972.     }
  973.  
  974.     while(tl = texthead) {
  975.         texthead = tl->next_line;
  976.         free((genericptr_t) tl);
  977.     }
  978. }
  979.  
  980. #endif /* OVL0 */
  981. #ifdef OVLB
  982.  
  983. #ifdef WIZARD
  984. static
  985. void
  986. wiz_help()
  987. {
  988.     cornline(0, "Wizard-Mode Quick Reference:");
  989.     cornline(1, "^E  ==  detect secret doors and traps.");
  990.     cornline(1, "^F  ==  do magic mapping.");
  991.     cornline(1, "^G  ==  create monster.");
  992.     cornline(1, "^I  ==  identify items in pack.");
  993.     cornline(1, "^O  ==  tell locations of special levels.");
  994.     cornline(1, "^T  ==  do intra-level teleport.");
  995.     cornline(1, "^V  ==  do trans-level teleport.");
  996.     cornline(1, "^W  ==  make wish.");
  997.     cornline(1, "^X  ==  show intrinsic attributes.");
  998.     cornline(1, "");
  999.     cornline(2, "");
  1000. }
  1001. #endif
  1002.  
  1003. static void
  1004. help_menu() {
  1005.     cornline(0, "Information available:");
  1006.     cornline(1, "a.  Long description of the game and commands.");
  1007.     cornline(1, "b.  List of game commands.");
  1008.     cornline(1, "c.  Concise history of NetHack.");
  1009.     cornline(1, "d.  Info on a character in the game display.");
  1010.     cornline(1, "e.  Info on what a given key does.");
  1011.     cornline(1, "f.  List of game options.");
  1012.     cornline(1, "g.  Longer explanation of game options.");
  1013.     cornline(1, "h.  List of extended commands.");
  1014.     cornline(1, "i.  The NetHack license.");
  1015. #ifdef MACOS
  1016.     cornline(1, "j.  Macintosh primer.");
  1017. #endif
  1018. #ifdef WIZARD
  1019.     if (wizard)
  1020. # ifdef MACOS
  1021.         cornline(1, "k.  List of wizard-mode commands.");
  1022. # else
  1023.         cornline(1, "j.  List of wizard-mode commands.");
  1024. # endif
  1025. #endif
  1026.     cornline(1, "");
  1027. #ifdef WIZARD
  1028.     if (wizard)
  1029. # ifdef MACOS
  1030.         cornline(1, "Select one of a,b,c,d,e,f,g,h,i,j,k or ESC: ");
  1031. # else
  1032.         cornline(1, "Select one of a,b,c,d,e,f,g,h,i,j or ESC: ");
  1033. # endif
  1034.     else
  1035. #endif
  1036. #ifdef MACOS
  1037.         cornline(1, "Select one of a,b,c,d,e,f,g,h,i,j or ESC: ");
  1038. #else
  1039.         cornline(1, "Select one of a,b,c,d,e,f,g,h,i or ESC: ");
  1040. #endif
  1041.     cornline(-1,"");
  1042. }
  1043.  
  1044. STATIC_OVL boolean
  1045. clear_help(c)
  1046. char c;
  1047. {
  1048.     /* those valid_help characters which do not correspond to help routines
  1049.      * that redraw the whole screen on their own.  if we always clear the
  1050.      * help menu, we end up restoring the part of the maze underneath the
  1051.      * help menu when the last page of a long help file is displayed with
  1052.      * an external pager.
  1053.      *
  1054.      * When whole_screen is FALSE and the internal pager is used, the
  1055.      * screen is big enough so that the maze is left in place during paging
  1056.      * and the paging occurs in the lower part of the screen.  In this case
  1057.      * the pager clears out the part it wrote over when it exits but it
  1058.      * doesn't redraw the whole screen.  So all characters require that
  1059.      * the help menu be cleared.
  1060.      *
  1061.      * When an external pager is used, the screen is always cleared.
  1062.      * However, the "f" and "h" help options always use the internal
  1063.      * pager even if DEF_PAGER is defined.
  1064.      *                        - Bob Wilber  wilber@homxb.att.com  10/20/89
  1065.      */
  1066.     return(index(quitchars,c) || c == 'd' || c == 'e'
  1067. #ifdef DEF_PAGER
  1068.             || (!whole_screen && (c == 'f' || c == 'h'))
  1069. #else
  1070.             || !whole_screen
  1071. #endif
  1072. #ifdef WIZARD
  1073. # ifdef MACOS
  1074.         || c == 'k'
  1075. # else
  1076.         || c == 'j'
  1077. # endif
  1078. #endif
  1079.         );
  1080. }
  1081.  
  1082. STATIC_OVL boolean
  1083. valid_help(c)
  1084. char c;
  1085. {
  1086. #ifdef WIZARD
  1087. # ifdef MACOS
  1088.     return ((c >= 'a' && c <= (wizard ? 'k' : 'j')) || index(quitchars,c));
  1089. # else
  1090.     return ((c >= 'a' && c <= (wizard ? 'j' : 'i')) || index(quitchars,c));
  1091. # endif
  1092. #else
  1093. # ifdef MACOS
  1094.     return ((c >= 'a' && c <= 'j') || index(quitchars,c));
  1095. # else
  1096.     return ((c >= 'a' && c <= 'i') || index(quitchars,c));
  1097. # endif
  1098. #endif
  1099. }
  1100.  
  1101. int
  1102. dohelp()
  1103. {
  1104. #ifdef MACOS
  1105.     term_info    *t;
  1106.  
  1107.     macflags &= ~fDoNonKeyEvt;
  1108.     t = (term_info *)GetWRefCon(HackWindow);
  1109.     SetVol((StringPtr)NULL,
  1110.         (t->auxFileVRefNum) ? t->auxFileVRefNum : t->recordVRefNum);
  1111. #endif
  1112.     help_menu();
  1113.     if (!index(quitchars, hc)) {
  1114.         switch(hc) {
  1115.             case 'a':  (void) page_file(HELP, FALSE);  break;
  1116.             case 'b':  (void) page_file(SHELP, FALSE);  break;
  1117.             case 'c':  (void) dohistory();  break;
  1118.             case 'd':  (void) dowhatis();  break;
  1119.             case 'e':  (void) dowhatdoes();  break;
  1120.             case 'f':  option_help();  break;
  1121.             case 'g':  (void) page_file(OPTIONFILE, FALSE);  break;
  1122.             case 'h':  (void) doextlist();  break;
  1123.             case 'i':  (void) page_file(LICENSE, FALSE);  break;
  1124. #ifdef WIZARD
  1125. # ifdef MACOS
  1126.             case 'j':  (void) page_file(MACHELP, FALSE);  break;
  1127.             case 'k':  wiz_help();  break;
  1128. # else
  1129.             case 'j':  wiz_help();  break;
  1130. # endif
  1131. #endif
  1132.         }
  1133.     }
  1134. #ifdef MACOS
  1135.     SetVol((StringPtr)NULL, t->recordVRefNum);
  1136.     macflags |= fDoNonKeyEvt;
  1137. #endif
  1138.     return 0;
  1139. }
  1140.  
  1141. int
  1142. dohistory()
  1143. {
  1144.     (void) page_file(HISTORY, FALSE);
  1145.     return 0;
  1146. }
  1147.  
  1148. int
  1149. page_file(fnam, silent)    /* return: 0 - cannot open fnam; 1 - otherwise */
  1150. register const char *fnam;
  1151. boolean silent;
  1152. {
  1153. #ifdef DEF_PAGER            /* this implies that UNIX is defined */
  1154.       {
  1155.     /* use external pager; this may give security problems */
  1156.  
  1157.     register int fd = open(fnam, 0);
  1158.  
  1159.     if(fd < 0) {
  1160.         if(!silent) pline("Cannot open %s.", fnam);
  1161.         return(0);
  1162.     }
  1163.     if(child(1)){
  1164.         /* Now that child() does a setuid(getuid()) and a chdir(),
  1165.            we may not be able to open file fnam anymore, so make
  1166.            it stdin. */
  1167.         (void) close(0);
  1168.         if(dup(fd)) {
  1169.             if(!silent) Printf("Cannot open %s as stdin.\n", fnam);
  1170.         } else {
  1171.             (void) execl(catmore, "page", NULL);
  1172.             if(!silent) Printf("Cannot exec %s.\n", catmore);
  1173.         }
  1174.         exit(1);
  1175.     }
  1176.     (void) close(fd);
  1177.       }
  1178. #else
  1179.       {
  1180.     FILE *f;            /* free after Robert Viduya */
  1181. #ifdef OS2_CODEVIEW
  1182.     char tmp[PATHLEN];
  1183.  
  1184.     Strcpy(tmp,hackdir);
  1185.     append_slash(tmp);
  1186.     Strcat(tmp,fnam);
  1187.     if ((f = fopen (tmp, "r")) == (FILE *) 0) {
  1188. #else
  1189. # ifdef MACOS
  1190.     if ((f = fopen (fnam, "r")) == (FILE *) 0)
  1191.         f = openFile(fnam, "r");
  1192.     if (!f) {
  1193. # else
  1194.     if ((f = fopen (fnam, "r")) == (FILE *) 0) {
  1195. # endif
  1196. #endif
  1197.         if(!silent) {
  1198.             home(); perror (fnam); flags.toplin = 1;
  1199.             pline ("Cannot open %s.", fnam);
  1200.         }
  1201.         return(0);
  1202.     }
  1203.     page_more(f, 0);
  1204.       }
  1205. #endif /* DEF_PAGER /**/
  1206.  
  1207.     return(1);
  1208. }
  1209.  
  1210. #ifdef UNIX
  1211. #ifdef SHELL
  1212. int
  1213. dosh(){
  1214. register char *str;
  1215.     if(child(0)) {
  1216.         if(str = getenv("SHELL"))
  1217.             (void) execl(str, str, NULL);
  1218.         else
  1219.             (void) execl("/bin/sh", "sh", NULL);
  1220.         pline("sh: cannot execute.");
  1221.         exit(1);
  1222.     }
  1223.     return 0;
  1224. }
  1225. #endif /* SHELL /**/
  1226.  
  1227. #if defined(SHELL) || defined(DEF_PAGER) || defined(DEF_MAILREADER)
  1228. int
  1229. child(wt)
  1230. int wt;
  1231. {
  1232. register int f = fork();
  1233.     if(f == 0){        /* child */
  1234.         settty(NULL);        /* also calls end_screen() */
  1235.         (void) setgid(getgid());
  1236.         (void) setuid(getuid());
  1237. #ifdef CHDIR
  1238.         (void) chdir(getenv("HOME"));
  1239. #endif
  1240.         return(1);
  1241.     }
  1242.     if(f == -1) {    /* cannot fork */
  1243.         pline("Fork failed.  Try again.");
  1244.         return(0);
  1245.     }
  1246.     /* fork succeeded; wait for child to exit */
  1247.     (void) signal(SIGINT,SIG_IGN);
  1248.     (void) signal(SIGQUIT,SIG_IGN);
  1249.     (void) wait(
  1250. #if defined(BSD) || defined(ULTRIX)
  1251.         (union wait *)
  1252. #else
  1253.         (int *)
  1254. #endif
  1255.         0);
  1256.     gettty();
  1257.     setftty();
  1258.     (void) signal(SIGINT, (SIG_RET_TYPE) done1);
  1259. #ifdef WIZARD
  1260.     if(wizard) (void) signal(SIGQUIT,SIG_DFL);
  1261. #endif
  1262.     if(wt) {
  1263.         boolean so;
  1264.  
  1265.         cmov(1, LI);    /* get prompt in reasonable place */
  1266.         so = flags.standout;
  1267.         flags.standout = 1;
  1268.         more();
  1269.         flags.standout = so;
  1270.     }
  1271.     docrt();
  1272.     return(0);
  1273. }
  1274. #endif
  1275. #endif /* UNIX /**/
  1276.  
  1277. #endif /* OVLB */
  1278.